package com.yc.testupload.crawler.strategy;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.yc.testupload.crawler.test.Question;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 牛客网专用爬虫策略
 */
public class NiuKeCrawlingStrategy implements CrawlingStrategy {
    private static final Logger logger = LogManager.getLogger(NiuKeCrawlingStrategy.class);
    
    // 牛客网域名列表，用于判断URL是否适用于此策略
    private static final String[] NIUKER_DOMAINS = {
        "nowcoder.com", "niuke.com", "nowcoder.net", "niuke.net"
    };
    
    // 最大题目数量限制
    private static final int MAX_QUESTIONS = 30;
    
    @Override
    public boolean isApplicable(String url) {
        if (url == null || url.isEmpty()) {
            return false;
        }
        
        // 检查URL是否包含牛客网的域名
        for (String domain : NIUKER_DOMAINS) {
            if (url.toLowerCase().contains(domain)) {
                logger.info("URL {} 符合牛客网爬虫策略", url);
                return true;
            }
        }
        
        return false;
    }
    
    @Override
    public String extractQuestions(String htmlContent) {
        logger.info("开始使用牛客网爬虫策略提取面试题");
        List<Question> questions = new ArrayList<>();
        StringBuilder result = new StringBuilder();
        
        try {
            // 先尝试从JavaScript状态对象中提取结构化数据
            List<Question> structuredQuestions = extractQuestionsFromJsState(htmlContent);
            if (!structuredQuestions.isEmpty()) {
                logger.info("成功从JS状态对象提取到 {} 道面试题", structuredQuestions.size());
                questions = structuredQuestions;
            } else {
                // 如果结构化提取失败，尝试从纯文本中提取
                logger.info("从JS状态对象提取失败，尝试从HTML内容提取");
                List<Question> textQuestions = extractQuestionsFromPlainText(htmlContent);
                if (!textQuestions.isEmpty()) {
                    logger.info("成功从HTML内容提取到 {} 道面试题", textQuestions.size());
                    questions = textQuestions;
                }
            }
            
            // 格式化提取的题目和答案
            if (!questions.isEmpty()) {
                for (Question question : questions) {
                    result.append("题目 ").append(question.getNumber()).append(": ").append(question.getTitle()).append("\n");
                    result.append(question.getContent()).append("\n");
                    result.append("答案: ").append(question.getAnswer()).append("\n\n");
                }
            } else {
                logger.warn("未能从牛客网页面提取到任何面试题");
            }
        } catch (Exception e) {
            logger.error("使用牛客网爬虫策略提取面试题时发生异常", e);
        }
        
        return result.toString();
    }
    
    /**
     * 从JavaScript状态对象中提取结构化的题目和答案
     */
    private List<Question> extractQuestionsFromJsState(String htmlContent) {
        List<Question> questions = new ArrayList<>();
        
        try {
            // 尝试从window.__INITIAL_STATE__提取数据
            Pattern statePattern = Pattern.compile("window\\.__INITIAL_STATE__\\s*=\\s*(.*?);\\s*</script>", Pattern.DOTALL);
            Matcher stateMatcher = statePattern.matcher(htmlContent);
            
            if (stateMatcher.find()) {
                String jsonData = stateMatcher.group(1);
                logger.info("找到INITIAL_STATE数据");
                
                // 尝试从JSON数据中提取题目
                questions.addAll(extractQuestionsFromJsonData(jsonData));
                if (!questions.isEmpty()) {
                    return questions;
                }
            }
            
            // 尝试从Vue状态对象提取数据
            Pattern vuePattern = Pattern.compile("new\\s+Vue\\s*\\(\\{.*?data\\s*:\\s*(\\{.*?\\}),", Pattern.DOTALL);
            Matcher vueMatcher = vuePattern.matcher(htmlContent);
            
            if (vueMatcher.find()) {
                String jsonData = vueMatcher.group(1);
                logger.info("找到Vue数据");
                
                // 尝试从JSON数据中提取题目
                questions.addAll(extractQuestionsFromJsonData(jsonData));
                if (!questions.isEmpty()) {
                    return questions;
                }
            }
            
            // 尝试从任何<script>标签中提取JSON数据
            Pattern scriptPattern = Pattern.compile("<script[^>]*>\\s*([^<]*?)\\s*</script>", Pattern.DOTALL);
            Matcher scriptMatcher = scriptPattern.matcher(htmlContent);
            
            while (scriptMatcher.find() && questions.isEmpty()) {
                String scriptContent = scriptMatcher.group(1);
                if (scriptContent.contains("questionList") || 
                    scriptContent.contains("questions") || 
                    scriptContent.contains("problems")) {
                    
                    logger.info("找到可能包含题目数据的script标签");
                    questions.addAll(extractQuestionsFromJsonData(scriptContent));
                }
            }
            
        } catch (Exception e) {
            logger.error("从JS状态对象提取数据时发生异常", e);
        }
        
        return questions;
    }
    
    /**
     * 从JSON数据中提取题目
     */
    private List<Question> extractQuestionsFromJsonData(String jsonData) {
        List<Question> questions = new ArrayList<>();
        
        try {
            // 尝试提取数据的不同路径
            String[][] paths = {
                {"prefetchData", "paperDetail"}, {"questionList"}, {"questions"},
                {"data", "list"}, {"quizList"}, {"examPaper"}, {"paperData"},
                {"companyList"}, {"problemList"}, {"topics"}
            };
            
            for (String[] path : paths) {
                if (questions.size() >= MAX_QUESTIONS) break;
                
                String searchPath = buildSearchPath(path);
                Pattern itemPattern = Pattern.compile(
                    searchPath + "\\s*:\\s*\\[([^\\]]*?)\\]", 
                    Pattern.DOTALL
                );
                Matcher itemMatcher = itemPattern.matcher(jsonData);
                
                if (itemMatcher.find()) {
                    String itemsData = itemMatcher.group(1);
                    logger.info("从路径 {} 找到题目列表数据", String.join(".", path));
                    
                    // 分割题目项
                    Pattern singleItemPattern = Pattern.compile("{[^{}]*?},?");
                    Matcher singleItemMatcher = singleItemPattern.matcher(itemsData);
                    
                    while (singleItemMatcher.find() && questions.size() < MAX_QUESTIONS) {
                        String itemJson = singleItemMatcher.group().replaceAll(",$", "");
                        Question question = extractQuestionFromItemJson(itemJson);
                        if (question != null && question.getContent() != null && 
                            !question.getContent().isEmpty() && question.getContent().length() >= 10) {
                            questions.add(question);
                        }
                    }
                    
                    if (!questions.isEmpty()) {
                        break;
                    }
                }
            }
            
            // 如果上面的方法都没找到，直接从JSON中提取题目文本
            if (questions.isEmpty()) {
                logger.info("尝试直接从JSON文本中提取题目");
                questions.addAll(extractQuestionsFromRawJson(jsonData));
            }
            
        } catch (Exception e) {
            logger.error("从JSON数据提取题目时发生异常", e);
        }
        
        return questions;
    }
    
    /**
     * 构建JSON搜索路径
     */
    private String buildSearchPath(String[] pathParts) {
        StringBuilder path = new StringBuilder();
        for (int i = 0; i < pathParts.length; i++) {
            if (i > 0) {
                path.append("\\.\\s*");
            }
            path.append("(?:\\\"\\\\s*:\\s*\\\\\"")
                .append(pathParts[i])
                .append("\\\\s*\\\"")
                .append("|'\\\\s*:\\s*'")
                .append(pathParts[i])
                .append("\\\\s*'")
                .append("|\\\\s*:\\s*")
                .append(pathParts[i])
                .append(")");
        }
        return path.toString();
    }
    
    /**
     * 从单个题目JSON中提取题目
     */
    private Question extractQuestionFromItemJson(String itemJson) {
        Question question = new Question();
        
        try {
            // 提取题目ID/编号
            String number = extractFieldFromJson(itemJson, "id", "questionId", "number", "index");
            if (number != null) {
                question.setNumber(number);
            }
            
            // 提取题目标题
            String title = extractFieldFromJson(itemJson, "title", "name", "stemTitle");
            if (title != null) {
                question.setTitle(cleanHtml(title));
            }
            
            // 提取题目内容
            String content = extractFieldFromJson(itemJson, "content", "stem", "question", "description");
            if (content != null) {
                // 清理HTML标签
                content = cleanHtml(content);
                question.setContent(content);
                
                // 如果没有标题，使用内容的前60个字符作为标题
                if (title == null) {
                    question.setTitle(content.length() > 60 ? content.substring(0, 60) + "..." : content);
                }
            }
            
            // 提取答案/解析
            String answer = extractFieldFromJson(itemJson, "answer", "analysis", "explanation", 
                                               "referenceAnswer", "solution", "detail");
            if (answer != null) {
                // 清理HTML标签
                answer = cleanHtml(answer);
                question.setAnswer(answer);
            }
            
            // 如果没有找到答案，尝试查找选项和正确答案
            if (answer == null || answer.isEmpty()) {
                extractOptionsAndAnswer(itemJson, question);
            }
            
        } catch (Exception e) {
            logger.error("解析单个题目JSON时发生异常", e);
            return null;
        }
        
        return question;
    }
    
    /**
     * 从JSON字符串中提取指定字段的值
     */
    private String extractFieldFromJson(String json, String... fieldNames) {
        for (String fieldName : fieldNames) {
            // 匹配字符串值（处理转义引号）
            // 修复正则表达式中的转义字符
            Pattern stringPattern = Pattern.compile(
                "(?:\\\\\"|\\\\')" + fieldName + "(?:\\\\\"|\\\\')\\\\s*:\\s*(?:\\\\\"|\\\\')((?:.*?)(?:\\\\\\\\|.*?)*?)(?:\\\\\"|\\\\')", 
                Pattern.CASE_INSENSITIVE
            );
            Matcher stringMatcher = stringPattern.matcher(json);
            
            if (stringMatcher.find()) {
                return stringMatcher.group(1);
            }
            
            // 匹配非字符串值（数字、布尔等）
            // 修复正则表达式中的转义字符
            Pattern nonStringPattern = Pattern.compile(
                "(?:\\\\\"|\\\\')" + fieldName + "(?:\\\\\"|\\\\')\\\\s*:\\s*([^,}\\]]*)", 
                Pattern.CASE_INSENSITIVE
            );
            Matcher nonStringMatcher = nonStringPattern.matcher(json);
            
            if (nonStringMatcher.find()) {
                return nonStringMatcher.group(1).trim();
            }
        }
        return null;
    }
    
    /**
     * 从题目JSON中提取选项和正确答案
     */
    private void extractOptionsAndAnswer(String itemJson, Question question) {
        try {
            StringBuilder optionsBuilder = new StringBuilder();
            
            // 查找options字段
            String optionsStr = extractFieldFromJson(itemJson, "options", "choices");
            if (optionsStr != null && !optionsStr.isEmpty()) {
                // 解析选项字符串（可能是JSON数组）
                if (optionsStr.startsWith("[")) {
                    // 尝试解析选项数组
                    Pattern optionPattern = Pattern.compile("{[^{}]*?}");
                    Matcher optionMatcher = optionPattern.matcher(optionsStr);
                    
                    while (optionMatcher.find()) {
                        String optionJson = optionMatcher.group();
                        String optionKey = extractFieldFromJson(optionJson, "key", "optionKey");
                        String optionValue = extractFieldFromJson(optionJson, "value", "content");
                        
                        if (optionValue != null) {
                            if (optionKey != null) {
                                optionsBuilder.append(optionKey).append(". ").append(cleanHtml(optionValue)).append("\n");
                            } else {
                                optionsBuilder.append(cleanHtml(optionValue)).append("\n");
                            }
                        }
                    }
                } else {
                    // 清理并直接添加
                    optionsBuilder.append(cleanHtml(optionsStr));
                }
            }
            
            // 如果没找到options字段，尝试查找单个选项字段
            if (optionsBuilder.length() == 0) {
                for (char optionKey = 'A'; optionKey <= 'E'; optionKey++) {
                    String optionFieldName = "option" + optionKey;
                    String optionValue = extractFieldFromJson(itemJson, optionFieldName);
                    
                    if (optionValue != null && !optionValue.isEmpty()) {
                        optionsBuilder.append(optionKey).append(". ").append(cleanHtml(optionValue)).append("\n");
                    }
                }
            }
            
            String options = optionsBuilder.toString().trim();
            if (!options.isEmpty()) {
                // 添加到题目内容中
                question.setContent(question.getContent() + "\n\n选项：\n" + options);
            }
            
            // 查找正确答案
            String correctAnswer = extractFieldFromJson(itemJson, "correctAnswer", "rightAnswer", "answer", "correct");
            if (correctAnswer != null && !correctAnswer.isEmpty()) {
                question.setAnswer("正确答案：" + cleanHtml(correctAnswer));
            } else if (!options.isEmpty()) {
                question.setAnswer("正确答案未明确标记");
            }
            
        } catch (Exception e) {
            logger.error("提取选项和答案时发生异常", e);
        }
    }
    
    /**
     * 从原始JSON中提取题目文本
     */
    private List<Question> extractQuestionsFromRawJson(String jsonData) {
        List<Question> questions = new ArrayList<>();
        
        try {
            // 清理JSON中的转义字符
            String cleanJson = jsonData.replaceAll("\\\\", "\\");
            
            // 提取包含题目关键词的文本块
            String[] keywords = {"题目", "问题", "题干", "QUESTION", "Problem", "编程题", "选择题", "简答题"};
            StringBuilder allText = new StringBuilder();
            
            for (String keyword : keywords) {
                Pattern textPattern = Pattern.compile(".*?" + keyword + ".*?", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
                Matcher textMatcher = textPattern.matcher(cleanJson);
                
                while (textMatcher.find() && allText.length() < 50000) { // 限制处理的文本大小
                    allText.append(textMatcher.group()).append("\n");
                }
            }
            
            // 如果没有提取到关键词，使用整个JSON文本
            if (allText.length() == 0) {
                allText.append(cleanJson);
            }
            
            String combinedText = cleanHtml(allText.toString());
            
            // 使用通用问答格式提取题目
            questions = extractCommonQuestionAnswerFormat(combinedText);
            
        } catch (Exception e) {
            logger.error("从原始JSON提取题目时发生异常", e);
        }
        
        return questions;
    }
    
    /**
     * 从纯文本中提取题目和答案
     */
    private List<Question> extractQuestionsFromPlainText(String htmlContent) {
        List<Question> questions = new ArrayList<>();
        
        try {
            // 移除HTML标签，减少处理的复杂度
            String text = cleanHtml(htmlContent);
            
            // 如果文本过长，分段处理以避免性能问题
            int maxLength = 50000; // 每段最大处理长度
            if (text.length() > maxLength) {
                logger.info("文本过长，分段处理");
                int start = 0;
                while (start < text.length() && questions.size() < MAX_QUESTIONS) {
                    int end = Math.min(start + maxLength, text.length());
                    String segment = text.substring(start, end);
                    List<Question> segmentQuestions = extractCommonQuestionAnswerFormat(segment);
                    
                    // 添加未重复的题目
                    for (Question q : segmentQuestions) {
                        if (!questions.contains(q) && questions.size() < MAX_QUESTIONS) {
                            questions.add(q);
                        }
                    }
                    
                    start = end;
                }
            } else {
                // 直接处理整个文本
                questions = extractCommonQuestionAnswerFormat(text);
            }
            
        } catch (Exception e) {
            logger.error("从纯文本提取题目时发生异常", e);
        }
        
        return questions;
    }
    
    /**
     * 使用通用问答格式提取题目
     */
    private List<Question> extractCommonQuestionAnswerFormat(String text) {
        List<Question> questions = new ArrayList<>();
        
        try {
            // 详细日志记录开始提取
            System.out.println("[DEBUG] 开始提取面试题，文本长度: " + text.length());
            
            // 改进的正则表达式，更精确地匹配面试题目格式
            // 匹配 "1. 题目内容" 或 "1、题目内容" 格式
            Pattern numberedPattern = Pattern.compile(
                "(\\d+)([、.])([\\s\\S]{20,}?)(?=(?:\\d+[、.])|答案|解析|正确选项|解题思路|参考答案|详解|$)",
                Pattern.CASE_INSENSITIVE
            );
            Matcher numberedMatcher = numberedPattern.matcher(text);
            
            int count = 0;
            int lastEnd = 0;
            
            // 用于存储已处理的题号，避免重复
            java.util.Set<Integer> processedNumbers = new java.util.HashSet<>();
            
            while (numberedMatcher.find(lastEnd) && count < MAX_QUESTIONS) {
                int start = numberedMatcher.start();
                int end = numberedMatcher.end();
                
                // 提取题号和分隔符
                String numberStr = numberedMatcher.group(1);
                String separator = numberedMatcher.group(2);
                // 提取题目内容（不包括序号）
                String content = numberedMatcher.group(3).trim();
                
                // 转换题号为整数
                int number;
                try {
                    number = Integer.parseInt(numberStr);
                    System.out.println("[DEBUG] 提取到题号: " + number + ", 内容长度: " + content.length());
                } catch (NumberFormatException e) {
                    System.out.println("[DEBUG] 跳过无效题号: " + numberStr);
                    lastEnd = end;
                    continue;
                }
                
                // 跳过重复的题号
                if (processedNumbers.contains(number)) {
                    System.out.println("[DEBUG] 跳过重复题号: " + number);
                    lastEnd = end;
                    continue;
                }
                
                // 确保内容不是空的或过于简短
                if (content.length() < 20) {
                    System.out.println("[DEBUG] 跳过太短的题目内容，长度: " + content.length());
                    lastEnd = end;
                    continue;
                }
                
                // 尝试查找答案部分
                String[] answerKeywords = {"答案：", "答案:", "解析：", "解析:", "正确选项", "解题思路", "参考答案", "详解"};
                int answerStart = -1;
                String answerKeyword = null;
                
                for (String keyword : answerKeywords) {
                    int pos = text.indexOf(keyword, end);
                    if (pos != -1 && (answerStart == -1 || pos < answerStart)) {
                        answerStart = pos;
                        answerKeyword = keyword;
                    }
                }
                
                // 查找下一个题目的开始位置
                Matcher nextMatcher = Pattern.compile("\\d+[、.]", Pattern.CASE_INSENSITIVE).matcher(text);
                int nextQuestionStart = -1;
                if (nextMatcher.find(end)) {
                    nextQuestionStart = nextMatcher.start();
                }
                
                // 提取答案内容
                String answerText = "";
                if (answerStart != -1) {
                    // 检查答案位置是否在下一个题目之前
                    if (nextQuestionStart == -1 || answerStart < nextQuestionStart) {
                        int answerEnd = nextQuestionStart != -1 ? nextQuestionStart : text.length();
                        answerText = text.substring(answerStart, answerEnd).trim();
                        // 限制答案长度，避免包含过多无关内容
                        if (answerText.length() > 1000) {
                            answerText = answerText.substring(0, 1000) + "...";
                        }
                    }
                    System.out.println("[DEBUG] 找到答案关键词: " + answerKeyword + ", 答案长度: " + answerText.length());
                } else {
                    System.out.println("[DEBUG] 未找到答案关键词");
                }
                
                // 清理题目内容
                String cleanedContent = cleanHtml(content);
                
                // 提取标题
                String title = cleanedContent.length() > 100 ? cleanedContent.substring(0, 100).trim() + "..." : cleanedContent.trim();
                
                // 创建题目对象
                Question question = new Question();
                question.setNumber(String.valueOf(number)); // 保持与原代码一致，使用字符串类型
                question.setTitle(title);
                question.setContent(cleanedContent);
                question.setAnswer(answerText.isEmpty() || answerText.length() < 10 ? "答案未找到，请手动添加" : cleanHtml(answerText));
                
                logger.info("提取到题目 {}: {}", number, title.substring(0, Math.min(title.length(), 50)) + (title.length() > 50 ? "..." : ""));
                questions.add(question);
                processedNumbers.add(number);
                
                // 更新lastEnd位置，避免重复提取
                lastEnd = Math.max(end, answerStart != -1 ? answerStart : nextQuestionStart != -1 ? nextQuestionStart : end + 20);
                count++;
                
                System.out.println("[DEBUG] 成功添加题目，当前总数: " + questions.size());
            }
            
            System.out.println("[DEBUG] 正则表达式提取完成，共提取题目数: " + questions.size());
            
        } catch (Exception e) {
            logger.error("使用通用问答格式提取题目时发生异常", e);
        }
        
        return questions;
    }
    
    /**
     * 清理HTML标签，只保留纯文本内容
     */
    private String cleanHtml(String html) {
        if (html == null || html.isEmpty()) {
            return "";
        }
        
        // 移除HTML标签
        String text = html.replaceAll("<[^>]*>", "").trim();
        
        // 移除多余的空白字符
        text = text.replaceAll("\\s+", " ");
        
        // 移除特殊的Unicode空白字符
        text = text.replaceAll("[\\u00A0\\u2007\\u202F]", " ");
        
        // 移除控制字符
        text = text.replaceAll("[\\p{Cntrl}&&[^\\r\\n\\t]]", "");
        
        // 移除JSON相关的特殊字符
        text = text.replaceAll("^\\{", "").replaceAll("\\}$", "");
        text = text.replaceAll("^\\[", "").replaceAll("\\]$", "");
        
        // 移除多余的引号
        // 修复转义字符
        text = text.replaceAll("^\\\\\\\\", "").replaceAll("\\\\\\\\$", "");
        text = text.replaceAll("^'", "").replaceAll("'$", "");
        
        return text;
    }
    
    /**
     * 尝试从文本中提取题目标题
     */
    private String extractQuestionTitle(String text) {
        // 匹配"1、标题" "问题1:标题" 等格式
        Pattern pattern = Pattern.compile("^(?:\\d+[、.\\s]*|[问题题目]\\d+[：:.]?)\\s*([^\\n]+?)(?=\\n|$|\\s*[\\(（][参考答案|解析]\\))");
        Matcher matcher = pattern.matcher(text);
        if (matcher.find()) {
            return matcher.group(1).trim();
        }
        
        // 如果没有明确的标题，使用文本的前60个字符作为标题
        return text.length() > 60 ? text.substring(0, 60) + "..." : text;
    }
    
    @Override
    public String extractTitle(String htmlContent) {
        try {
            Document document = Jsoup.parse(htmlContent);
            Element titleElement = document.selectFirst("title");
            if (titleElement != null && titleElement.text() != null && !titleElement.text().isEmpty()) {
                return titleElement.text();
            }
            
            // 如果没有标题元素，尝试从meta标签提取
            Element metaTitle = document.selectFirst("meta[name=title]");
            if (metaTitle != null) {
                String content = metaTitle.attr("content");
                if (content != null && !content.isEmpty()) {
                    return content;
                }
            }
            
            // 尝试从h1标签提取
            Element h1Element = document.selectFirst("h1");
            if (h1Element != null && h1Element.text() != null && !h1Element.text().isEmpty()) {
                return h1Element.text();
            }
            
        } catch (Exception e) {
            logger.error("提取页面标题时发生异常", e);
        }
        
        return "牛客网面试题";
    }
}